#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/skbuff.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include <linux/ip.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Device Driver Demo");
MODULE_AUTHOR("Prabhu Gurumurthy <pgurumur@gmail.com>");

#define DEVICE_NAME "netdevice"
#define DEVICE_NUMBER 82

static int devopen = 0;
static char msgbuf[100] = { 0 };
static int position = 0;
static struct nf_hook_ops nf_hook_opts;
static struct sk_buff *sockbuf;
static struct udphdr *udp;
static struct iphdr *ip;
static struct tcphdr *tcp;

static int open_netdevice(struct inode *, struct file *);
static int close_netdevice(struct inode *, struct file *);
static ssize_t read_netdevice(struct file *, char *, size_t, loff_t *);
static ssize_t write_netdevice(struct file *, const char *, size_t, loff_t *);
unsigned int capture(unsigned int, struct sk_buff **, const struct net_device *,
      const struct net_device *, int (*okfn)(struct sk_buff *));

static struct file_operations fops =
{
   .read = read_netdevice,
   .open = open_netdevice,
   .write = write_netdevice,
   .release = close_netdevice,
};

int
init_module(void) {

   int retval = register_chrdev(DEVICE_NUMBER, DEVICE_NAME, &fops);
   if (retval < 0)
      printk(KERN_ALERT "NetDevice registration failed\n");
   else
      printk(KERN_ALERT "NetDevice registered\n");

   nf_hook_opts.hook = capture;
   nf_hook_opts.hooknum = NF_INET_PRE_ROUTING;
   nf_hook_opts.pf = PF_INET;
   nf_hook_opts.priority = NF_IP_PRI_FIRST;

   nf_register_hook(&nf_hook_opts);

   return retval;
}

void
cleanup_module(void)
{
   unregister_chrdev(DEVICE_NUMBER, DEVICE_NAME);
   nf_unregister_hook(&nf_hook_opts);
}

static int
open_netdevice(struct inode *node, struct file *fptr)
{
   devopen++;
   return 0;
}

static int
close_netdevice(struct inode *node, struct file *fptr)
{
   if (devopen > 0)
      devopen--;

   return 0;
}

static ssize_t
read_netdevice(struct file *fptr, char *buffer, size_t length, loff_t *off)
{
   short count = 0;
   while (length && (msgbuf[position] != 0)) {
      put_user(msgbuf[position], buffer++);
      count++;
      length--;
      position++;
   }

   return count;
}

static ssize_t
write_netdevice(struct file *fptr, const char *buffer, size_t length,
      loff_t *off)
{
   short index = length - 1;
   short count = 0;
   memset(msgbuf, 0, sizeof(msgbuf));
   position = 0;

   while (length > 0) {
      msgbuf[count++] = buffer[index--];
      length--;
   }

   return count;
}

unsigned int capture(unsigned int hooknum, struct sk_buff **skb,
      const struct net_device *in, const struct net_device *out,
      int (*okfn)(struct sk_buff *))
{
   return NF_ACCEPT;
}

